﻿// $Id: DataMapperUnion.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
#pragma warning disable 1587
/** \file
 * \brief Definition of Data Mapper unions.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */
#pragma warning restore 1587

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Usual methods used to implement <include file="../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" /> unions.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public static class DataMapperUnion
    {

        /// <summary>Returns the non null <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" />.</summary>
        /// <exception cref="ArgumentException">Raised if <paramref name="parts" /> does not contain a single non null element.</exception>
        public static IDomainModel Join(IDomainModel[] parts)
        {
            IDomainModel[] joins=Array.FindAll<IDomainModel>(
                parts,
                delegate(IDomainModel m) {
                    return m!=null;
                }
            );

            if (joins.Length>1)
                throw new ArgumentException();

            if (joins.Length==0)
                return null;

            return joins[0];
        }

        /// <summary>Joins an exclusive list of <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" />s dictionaries into one.</summary>
        /// <exception cref="ArgumentException">Raised if a <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> is referenced in more than one dictionary.</exception>
        public static Dictionary<TPrimaryKey, TDomainModel> Join<TPrimaryKey, TDomainModel>(Dictionary<TPrimaryKey, TDomainModel>[] parts)
            where TPrimaryKey:
                IPrimaryKey
            where TDomainModel:
                IDomainModel
        {
            Dictionary<TPrimaryKey, TDomainModel> join=new Dictionary<TPrimaryKey, TDomainModel>(parts[0]);
            for (int i=0; i<parts.Length; ++i)
                foreach (KeyValuePair<TPrimaryKey, TDomainModel> kvp in parts[i])
                    join.Add(kvp.Key, kvp.Value);

            return join;
        }
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Base class of a read-only <include file="../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />
    /// implemented as the union of several <include file="../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public abstract class DataMapperUnion<TDomainModel, TPrimaryKey, TDataTransferObject>:
        Salamanca.DataAccess.DataMapperBase<TDomainModel, TPrimaryKey, TDataTransferObject>
        where TDomainModel:
            DomainModel<TPrimaryKey, TDataTransferObject>
        where TPrimaryKey:
            IPrimaryKey,
            new()
        where TDataTransferObject:
            class, // SOAP serialization does not accept structs...
            IDataTransferObject,
            new()
    {

        /// <summary>Constructor.</summary>
        /// <param name="mappers">The <include file="../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s to join.</param>
        /// <param name="instanceCreator">Member used to create new instances for the type.</param>
        /// <param name="cacheManager">A cache manager. Must not be <c>null</c>.</param>
        /// <exception cref="ArgumentNullException">Raised if <paramref name="mappers" /> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Raised if <paramref name="mappers" /> does not contain at least 1 element.</exception>
        protected DataMapperUnion(ICollection<IDataMapper> mappers, DomainModelCreator<TDomainModel> instanceCreator, ICacheManager cacheManager):
            base(instanceCreator, cacheManager)
        {
            Debug.Assert(mappers!=null);
            if (mappers==null)
                throw new ArgumentNullException("mappers");
            Debug.Assert(mappers.Count>0);
            if (mappers.Count<1)
                throw new ArgumentException("mappers", Resources.SR.AtLeastOneElementInCollection);

            _Mappers=new IDataMapper[mappers.Count];
            mappers.CopyTo(_Mappers, 0);
        }

        /// <summary>Finds the <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> associated to the specified key.</summary>
        public override TDomainModel Find(TPrimaryKey key, Collections.DataMapperCollection dataMappers)
        {
            return (TDomainModel)DataMapperUnion.Join(
                Array.ConvertAll<IDataMapper, IDomainModel>(
                    GetMappers(),
                    delegate(IDataMapper mapper) {
                        return mapper.Find(key, dataMappers);
                    }
                )
            );
        }

        /// <summary>Refreshes the data of the specified <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</summary>
        protected override void Refresh(TDomainModel domainModel)
        {
            Array.ForEach<IDataMapper>(
                GetMappers(),
                delegate(IDataMapper mapper) {
                    mapper.Refresh(domainModel);
                }
            );
        }

        /// <summary>Gets the data associated to the specified <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" />.</summary>
        protected override IDomainModelDataTransferObject GetData(TDomainModel domainModel, bool deep)
        {
            if (domainModel==null)
                return null;

            return new DomainModelDataTransferObject<TDataTransferObject>(
                (TDataTransferObject)((ITransferable)domainModel).GetData(),
                (deep?((IDomainModel)domainModel).Childs:null),
                ((IDomainModel)domainModel).IsLoaded,
                ((IDomainModel)domainModel).IsModified,
                ((IDomainModel)domainModel).IsNew
            );
        }

        /// <summary>Assigns the specified data to the specified <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" />.</summary>
        protected override void SetData(TDomainModel domainModel, IDomainModelDataTransferObject data)
        {
            ((ITransferable)domainModel).SetData(data.Data);
            domainModel.SetLoaded(data.IsLoaded);
        }

        /// <summary>Gets the <include file="../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s joined in this union.</summary>
        protected IDataMapper[] GetMappers()
        {
            return _Mappers;
        }

        /// <summary>Deletes the data of the specified <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</summary>
        protected override void Delete(TDomainModel domainModel, IPersistenceContext context)
        {
            Array.ForEach<IDataMapper>(
                GetMappers(),
                delegate(IDataMapper mapper) {
                    if (mapper is IDataMapper)
                        ((IDataMapper)mapper).Delete(domainModel, context);
                }
            );
        }

        /// <summary>Saves the data of the specified <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</summary>
        protected override void Save(TDomainModel domainModel, IPersistenceContext context)
        {
            Array.ForEach<IDataMapper>(
                GetMappers(),
                delegate(IDataMapper mapper) {
                    if (mapper is IDataMapper)
                        ((IDataMapper)mapper).Save(domainModel, context);
                }
            );
        }

        private IDataMapper[] _Mappers;
    }

}
